import processing.core.*;
import processing.xml.*;

import java.applet.*;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
import java.awt.event.FocusEvent;
import java.awt.Image;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import java.util.zip.*;
import java.util.regex.*;

public class TexturedSphere {

	// Sphere Variables
	float R = 50;
	int xDetail = 40;
	int yDetail = 30;
	float[] xGrid = new float[xDetail + 1];
	float[] yGrid = new float[yDetail + 1];
	float[][][] allPoints = new float[xDetail + 1][yDetail + 1][3];

	// Rotation Variables
	float camDistance = -50;
	float rotationX = 100;
	float rotationY = 170;
	float velocityX = 0;
	float velocityY = 0;

	// Texture
	PImage texmap;

	// //////////////////////////////////////////////////////////////////////
	public void setup(PApplet applet) {

		//applet.size(700, 700, PApplet.OPENGL);
		applet.noStroke();

		texmap = applet.loadImage("Mercator-projection.jpg");

		setupSphere(R, xDetail, yDetail, applet);
	}

	// //////////////////////////////////////////////////////////////////////
	public void draw(PApplet applet, int x, int y) {

		applet.pushStyle();
		applet.pushMatrix();
		
		applet.background(0);
		applet.translate(x, y, camDistance);
		applet.rotateX(PApplet.radians(-rotationX));
		applet.rotateZ(PApplet.radians(270 - rotationY));
		applet.noStroke();

		drawSphere(texmap, applet);

		// Implements mouse control (interaction will be inverse when sphere is
		// upside down)
		rotationX += velocityX;
		rotationY += velocityY;
		velocityX *= 0.95f;
		velocityY *= 0.95f;
		if (applet.mousePressed) {
			velocityX += (applet.mouseY - applet.pmouseY) * 0.01f;
			velocityY -= (applet.mouseX - applet.pmouseX) * 0.01f;
		}
		
		applet.popStyle();
		applet.popMatrix();
	}

	// //////////////////////////////////////////////////////////////////////
	public void setupSphere(float R, int xDetail, int yDetail, PApplet pApplet) {

		// Create a 2D grid of standardized mercator coordinates
		for (int i = 0; i <= xDetail; i++) {
			xGrid[i] = i / (float) xDetail;
		}
		for (int i = 0; i <= yDetail; i++) {
			yGrid[i] = i / (float) yDetail;
		}

		pApplet.textureMode(PApplet.NORMALIZED);

		// Transform the 2D grid into a grid of points on the sphere, using the
		// inverse mercator projection
		for (int i = 0; i <= xDetail; i++) {
			for (int j = 0; j <= yDetail; j++) {
				allPoints[i][j] = mercatorPoint(R, xGrid[i], yGrid[j]);
			}
		}
	}

	// //////////////////////////////////////////////////////////////////////
	public float[] mercatorPoint(float R, float x, float y) {

		float[] thisPoint = new float[3];
		float phi = x * 2 * PApplet.PI;
		float theta = PApplet.PI - y * PApplet.PI;

		thisPoint[0] = R * PApplet.sin(theta) * PApplet.cos(phi);
		thisPoint[1] = R * PApplet.sin(theta) * PApplet.sin(phi);
		thisPoint[2] = R * PApplet.cos(theta);

		return thisPoint;
	}

	// //////////////////////////////////////////////////////////////////////
	public void drawSphere(PImage Map, PApplet pApplet) {

		for (int j = 0; j < yDetail; j++) {
			pApplet.beginShape(PApplet.TRIANGLE_STRIP);
			pApplet.texture(Map);
			for (int i = 0; i <= xDetail; i++) {
				pApplet.vertex(allPoints[i][j + 1][0], allPoints[i][j + 1][1],
						allPoints[i][j + 1][2], xGrid[i], yGrid[j + 1]);
				pApplet.vertex(allPoints[i][j][0], allPoints[i][j][1],
						allPoints[i][j][2], xGrid[i], yGrid[j]);
			}
			pApplet.endShape(PApplet.CLOSE);
		}
	}

	static public void main(String args[]) {
		PApplet.main(new String[] { "--present", "--bgcolor=#666666",
				"--stop-color=#cccccc", "TexturedSphere" });
	}
}
